package com.epam.rd.myCollections.list;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class MyArrayList implements MyList, RandomAccess {
	private Object[] array;
	private int numOfElements;
	private final double fillFactor = 0.75;

	private final static int INITIALFILLING = 10;

	public MyArrayList() {
		this(INITIALFILLING);
	}

	public MyArrayList(int initialCapacity) {
		array = (Object[]) new Object[initialCapacity];
	}

	public void add(int index, Object e) {
		if (verificationIndex(index)) {
			if (index == this.numOfElements) {
				add(e);
			} else {
				if (!checkFreeSpaceToInsert()) {
					expandTheArray();
				}
				System.arraycopy(array, index, array, index + 1,
						this.numOfElements - index);
				array[index] = e;
				this.numOfElements++;
			}
		}
	}

	public void add(Object e) {
		if (!checkFreeSpace()) {
			expandTheArray();
		}
		array[numOfElements] = e;
		this.numOfElements++;
	}

	public void addAll(int index, Object[] c) {
		// TODO Auto-generated method stub
		for (int i = c.length - 1; i >= 0; i--) {
			add(index, c[i]);
		}
	}

	public void addAll(Object[] c) {
		// TODO Auto-generated method stub
		for (Object o : c) {
			add(o);
		}
	}

	private boolean checkFreeSpace() {
		return (this.numOfElements < ((this.array.length) * (fillFactor)));
	}

	private boolean checkFreeSpaceToInsert() {
		return (this.numOfElements + 1 < ((this.array.length) * (fillFactor)));
	}

	public void ensureCapacity(int minCapacity) {
		while (this.array.length < minCapacity) {
			this.expandTheArray();
		}
	}

	public int getCapacity() {
		return this.array.length;
	}

	public void trimToSize() {
		Object[] trimArray = (Object[]) new Object[this.numOfElements];
		System.arraycopy(array, 0, trimArray, 0, this.numOfElements);
		array = trimArray;
	}

	public void clear() {
		Object[] newEmptyArray = (Object[]) new Object[this.INITIALFILLING];
		this.array = newEmptyArray;
		this.numOfElements = 0;
	}

	private void expandTheArray() {

		int oldSize = this.array.length;
		int newSize = Math.round((float) (this.fillFactor) * (oldSize)
				+ oldSize);
		Object[] newArray = (Object[]) new Object[newSize];
		System.arraycopy(this.array, 0, newArray, 0, oldSize);
		this.array = newArray;
	}

	public Object get(int index) {
		if (this.size() == 0) {
			throw new MyIndexOutOfBoundsException();
		}
		verificationIndex(index);
		return array[index];
	}

	public int indexOf(Object o) {
		if (this.size() != 0) {
			for (int i = 0; i < this.array.length; i++) {
				if (o.equals(array[i])) {
					return i;
				}
			}
		}
		return -1;
	}

	public boolean isEmpty() {
		return this.size() == 0;
	}

	public Iterator<Object> iterator() {
		return new ArrayListIterator();
	}

	private class ArrayListIterator implements Iterator {
		private int cursor; // index of next element to return

		public boolean hasNext() {
			return cursor != size();
		}

		public Object next() {
			int i = cursor;
			if (i >= size()) {
				throw new NoSuchElementException();
			}
			Object elementData = array[i];
			cursor = i + 1;
			return elementData;
		}

		public void remove() {
		}
	}

	public void remove(int index) {
		if (verificationIndex(index)) {
			System.arraycopy(this.array, index + 1, this.array, index,
					this.numOfElements - index);
			this.numOfElements--;
		}
	}

	public void set(int index, Object e) {
		if (this.size() == 0) {
			throw new MyIndexOutOfBoundsException();
		}
		if (verificationIndex(index)) {
			this.array[index] = e;
		}
	}

	public int size() {
		return this.numOfElements;
	}

	public Object[] toArray() {
		return this.array;
	}

	private boolean verificationIndex(int index) {
		if (index >= 0 && index <= this.numOfElements) {
			return true;
		} else {
			throw new MyIndexOutOfBoundsException();
		}
	}

}
